#include "netclient.h"
#include <QNetworkReply>
#include <QUuid>
#include <QUuid>
#include "../model/data.h"
#include "../model/datacenter.h"


using namespace model;


namespace network
{

NetClient::NetClient(model::DataCenter *dataCenter)
    :dataCenter(dataCenter)
{
    //不应该在此初始化websocket,放在MainWidget初始化的时候
    //initWebSocket();
}

void NetClient::ping()
{
    QNetworkRequest httpReq;
    httpReq.setUrl(QUrl(HTTP_URL + "/ping"));

    QNetworkReply* httpResp = httpClient.get(httpReq);
    connect(httpResp, &QNetworkReply::finished, this, [=]() {
        // 这里面, 说明响应已经回来了.
        if (httpResp->error() != QNetworkReply::NoError) {
            // 请求失败!
            LOG() << "HTTP 请求失败! " << httpResp->errorString();
            httpResp->deleteLater();
            return;
        }
        // 获取到响应的 body
        QByteArray body = httpResp->readAll();
        LOG() << "响应内容: " << body;
        httpResp->deleteLater();
    });
}

///////////////////////////////
/// 下列逻辑都是 Websocket 相关的操作
////////////////////////////////


void NetClient::initWebSocket()
{
    // 1. 准备好需要的信号槽
    connect(&websocketClient, &QWebSocket::connected, this, [=](){
        LOG() << "websocket 连接成功!";
        // 不要忘记! 在 websocket 连接成功之后, 发送身份认证消息!
        sendAuth();
    });

    connect(&websocketClient, &QWebSocket::disconnected, this, [=](){
        LOG() << "websocket 连接断开!";
    });

    connect(&websocketClient, &QWebSocket::errorOccurred, this, [=](QAbstractSocket::SocketError error){
            LOG() << "websocket 连接出错!" << error;
        });
    connect(&websocketClient, &QWebSocket::textMessageReceived, this, [=](const QString& message){
        LOG() << "websocket 收到文本消息!" << message;
    });
    connect(&websocketClient, &QWebSocket::binaryMessageReceived, this, [=](const QByteArray& byteArray){
        LOG() << "websocket 收到二进制消息!" << byteArray.length();
        shiyue_im::NotifyMessage notifyMessage;
        notifyMessage.deserialize(&serializer, byteArray);
        handleWsResponse(notifyMessage);

    });

    // 2. 和服务器真正建立连接
    websocketClient.open(WEBSOCKET_URL);

}
void NetClient::handleWsResponse(shiyue_im::NotifyMessage &notifyMessage)
{
    if (notifyMessage.notifyType() == shiyue_im::NotifyTypeGadget::NotifyType::CHAT_MESSAGE_NOTIFY)
    {
        // 收到消息
        // 1 把 pb 中的MessageInfo 转换成客户端自己的 Message
        Message message;
        message.load(notifyMessage.newMessageInfo().messageInfo());
        // 2 针对自己的 message 做进一步的处理
        handleWsMessage(message);
    }
    else if(notifyMessage.notifyType() == shiyue_im::NotifyTypeGadget::NotifyType::CHAT_SESSION_CREATE_NOTIFY)
    {
        //创建新的会话
        ChatSessionInfo chatSessionInfo;
        chatSessionInfo.load(notifyMessage.newChatSessionInfo().chatSessionInfo());

        handleWsSessionCreate(chatSessionInfo);
    }
    else if (notifyMessage.notifyType() == shiyue_im::NotifyTypeGadget::NotifyType::FRIEND_ADD_APPLY_NOTIFY)
    {
        // 添加好友申请
        UserInfo userInfo;
        userInfo.load(notifyMessage.friendAddApply().userInfo());
        handleWsAddFriendApply(userInfo);
    }
    else if (notifyMessage.notifyType() == shiyue_im::NotifyTypeGadget::NotifyType::FRIEND_ADD_PROCESS_NOTIFY)
    {

        // 添加好友申请的处理结果通知
        UserInfo userInfo;
        userInfo.load(notifyMessage.friendProcessResult().userInfo());
        bool agree = notifyMessage.friendProcessResult().agree();
        handleWsAddFriendProcess(userInfo, agree);
    }
    else if (notifyMessage.notifyType() == shiyue_im::NotifyTypeGadget::NotifyType::FRIEND_REMOVE_NOTIFY)
    {
        // 删除好友通知
        const QString& userId = notifyMessage.friendRemove().userId();
        handleWsRemoveFriend(userId);
    }
}


void NetClient::handleWsMessage(const model::Message &message)
{
      // 这里要考虑两个情况

    QList<Message>* messageList = dataCenter->getRecentMessageList(message.chatSessionId);
      if (messageList == nullptr)
    {
      // 1. 如果当前这个消息所属会话, 里面的消息列表, 没有在本地加载,此时就需要通过网络加载消息列表
          connect(dataCenter, &DataCenter::getRecentMessageListDoneNoUi, this, &NetClient::receiveMessage, Qt::UniqueConnection);
          dataCenter->getRecentMessageListAsync(message.chatSessionId, false);
    }
      else {
          // 2 如果当前这个消息所属会话,里面的消息已经在本地加载了,直接把这个消息尾插到消息列表中即可
        messageList->push_back(message);
          this->receiveMessage(message.chatSessionId);

    }


      // 需要进一步判定,当前选中的会话,是否就是收到消息的会话
      // 如果是就把消息显示到消息展示区,同时更新会话列表的消息预览
      // 如果不是,就只把消息更新在会话列表里的消息预览里,同时新增未读消息计数


}

void NetClient::handleWsRemoveFriend(const QString &userId)
{
    // 1. 删除数据.DataCenter 好友列表数据
    dataCenter->removeFriend(userId);
    // 2. 通知界面变化,更新好友会话列表
    emit dataCenter->deleteFriendDone();
}

void NetClient::handleWsAddFriendApply(const model::UserInfo &userInfo)
{
    //1. DataCenter 中有一个好友申请列表,需要把这个数据添加到好友申请列表中
    QList<UserInfo>* applyList = dataCenter->getApplyList();
    if (applyList == nullptr) {
        LOG() << "客户端没有加载到好友申请列表!";
        return;
    }

    // 把新的元素放在列表前面
    applyList->push_front(userInfo);

    // 2. 通知界面更新
    emit dataCenter->receiveFriendApplyDone();



}

void NetClient::handleWsAddFriendProcess(const model::UserInfo &userInfo, bool agree)
{
    if (agree) {
        // 对方同意你的好友申请
        QList<UserInfo>* friendList = dataCenter->getFriendList();
        if (friendList == nullptr) {
            LOG() << "客户端没有加载好友列表";
                return;
        }
        friendList->push_front(userInfo);

        //同时更新界面
        emit dataCenter->receiveFriendProcessDone(userInfo.nickname, agree);
    } else {
        //对方未同意好友申请
        emit dataCenter->receiveFriendProcessDone(userInfo.nickname, agree);
    }
}

void NetClient::handleWsSessionCreate(const model::ChatSessionInfo &chatSessionInfo)
{
    // 把这个 ChatSessionInfo添加到会话列表
    QList<ChatSessionInfo>* chatSessionList = dataCenter->getChatSessionList();
    if (chatSessionList == nullptr) {
        LOG() << "客户端没有加载会话列表";
        return;
    }

    //新的元素加到列表头部
    chatSessionList->push_front(chatSessionInfo);
    //发送一个信号,通知界面更新
    emit dataCenter->receiveSessionCreateDone();

}



///////////////////////////////
/// 下列逻辑都是 HTTP 相关的操作
////////////////////////////////

void NetClient::sendAuth()
{
    shiyue_im::ClientAuthenticationReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(dataCenter->getLoginSessionId());
    QByteArray body = req.serialize(&serializer);
    websocketClient.sendBinaryMessage(body);
    LOG() <<"[ws身份认证] requestId=" << req.requestId() << ", loginSessionId=" << req.sessionId();
}

QString NetClient::makeRequestId()
{
    // 基本要求确保每个请求的id不重复(唯一的)
    // 通过UUID 来实现上述效果
    return "R" + QUuid::createUuid().toString().sliced(25, 12);
}

//在这个函数内部,把发送Http请求操作封装一下
QNetworkReply *NetClient::sendHttpRequest(const QString &apiPath, const QByteArray &body)
{
    QNetworkRequest httpReq;
    httpReq.setUrl(QUrl(HTTP_URL + apiPath));
    httpReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-protobuf");

    QNetworkReply* httpResp = httpClient.post(httpReq, body);
    return httpResp;
}

//在这个函数内部完成网络通信接口
void NetClient::getMyself(const QString &loginSessionId)
{
    // 1 构造出 HTTP 请求 body 部分
    shiyue_im::GetUserInfoReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG() << "[获取个人信息] 发送请求 requestId=" << req.requestId() << ", loginSessionId= " <<  loginSessionId;

    // 2 构造出 HTTP 请求, 并发送出去
    QNetworkReply* httpResp = sendHttpRequest("/service/user/get_user_info", body);
    // 3 通过信号槽获取到当前响应, finished信号表示响应已经返回到客户端了
    connect(httpResp, &QNetworkReply::finished, this, [=](){
    // 3.1先处理响应对象
        bool ok = false;
        QString reason;
        auto resp = handleHttpResponse<shiyue_im::GetUserInfoRsp>(httpResp, &ok, &reason);

        //判定 响应是否正确
        if (!ok)
        {
            LOG() << "[获取个人信息] 出错! requestId= " << req.requestId() << "reason=" << reason;
            return;
        }

        //把响应的数据, 保存到 DataCenter 中
        dataCenter->resetMyself(resp);

        // 通知调用逻辑, 响应已经处理完了, 仍然通过信号槽,通知
        emit dataCenter->getMyselfDone();

        // 打印日志
        LOG() << "[获取个人信息] 处理响应requestId=" << req.requestId();

    });

 }

void NetClient::getFriendList(const QString &loginSessionId)
{
    // 1 通过 protobuf 构造 body
    shiyue_im::GetFriendListReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG() << "[获取好友列表] 发送请求 requestId=" << req.requestId() << ", loginSessionId=" << loginSessionId;

    // 2 发送 HTTP 请求
   QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/get_friend_list", body);


   // 3 处理响应
   connect(httpResp,&QNetworkReply::finished, this, [=](){
       // 解析响应
       bool ok =false;
       QString reason;
       auto friendListResp =  this->handleHttpResponse<shiyue_im::GetFriendListRsp>(httpResp, &ok, &reason);
       // 判定响应是否正确
       if (!ok)
       {
           LOG() << "[获取好友列表]失败 ! requestId=" << req.requestId() << ", reason=" << reason;
           return;
       }
        // 把结果保存在 DataCenter 中
       dataCenter->resetFriendList(friendListResp);

       // 发送信号,通知界面, 当前这个操作完成了
       emit dataCenter->getFriendListDone();

       // 打印日志
       LOG() << "[获取好友列表] 处理响应 requestId=" << req.requestId();
   });


}

void NetClient::getChatSessionList(const QString &loginSessionId)
{

    // 1 通过protobuf 构造 body
    shiyue_im::GetChatSessionListReq req;
    req.setSessionId(loginSessionId);
    req.setRequestId(makeRequestId());
    QByteArray body = req.serialize(&serializer);
    LOG() << "[获取会话列表] 发送请求 requestId=" << req.requestId() <<", loginSessionid=" << loginSessionId;

    // 2 发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/friend/get_chat_session_list", body);

    LOG() << " 发送HTTP请求成功";

    // 3 针对响应进行处理
    connect(resp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;

        auto pbResp = this->handleHttpResponse<shiyue_im::GetChatSessionListRsp>(resp, &ok, &reason);

        // 判断响应是否正确
        if (!ok)
            {
            LOG() << "[获取会话列表]失败! reason= " <<reason;
            return;
        }

        // 把得到的数据, 写到 DataCenter里

        LOG() << " 处理获取会话响应开始";
        dataCenter->resetChatSessionList(pbResp);
        LOG() << " 通知调用者,此处响应完毕";
        // 通知调用者,此处响应完毕

        emit dataCenter->getChatSessionListDone();


        //打印日志
        LOG() << "[获取会话列表] 处理响应完毕! requestId=" << pbResp->requestId();
    });


}

void NetClient::getApplyList(const QString &loginSessionId)
{
     // 通过 protobuf 构造 bdoy
    shiyue_im::GetPendingFriendEventListReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body =  req.serialize(&serializer);
    LOG() << "[获取好友申请列表] 发送请求 request=" << req.requestId() <<", loginSessionId=" << loginSessionId;

    // 2 发送Http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/friend/get_pending_friend_events", body);

    // 3 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp =  this->handleHttpResponse<shiyue_im::GetPendingFriendEventListRsp>(resp, &ok, &reason);

        //判定结果是否出错
        if (!ok)
            {
            LOG() << "[获取好友申请列表失败, reason= " << reason;
            return;
        }

        // 拿到的数据写入到 DataCenter中
        dataCenter->resetApplyList(pbResp);

        //通知界面处理完毕
        emit dataCenter->getApplyListDone();

        //打印日志
        LOG() << "[获取好友申请列表] 处理响应完成! requestId=" << req.requestId();
    });

}

void NetClient::getRcentMessageList(const QString &loginSessionId, const QString &chatSessionId, bool updateUi)
{
    // 1 通过 protobuf 构造请求 body
    shiyue_im::GetRecentMsgReq req;
    req.setRequestId(makeRequestId());
    req.setChatSessionId(chatSessionId);
    req.setMsgCount(50); // 获取最近50条
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG() << "获取最近消息发送请求 requestId=" << req.requestId() << ", logInSessionId" << loginSessionId <<", chatSessionId" << chatSessionId;

   // 2 发送 http 请求

    QNetworkReply* resp = this->sendHttpRequest("/service/message_storage/get_recent", body);

   // 3 解析请求
    connect(resp, &QNetworkReply::finished, this, [=](){
      //解析响应, 反序列化
        bool ok = false;
      QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::GetRecentMsgRsp>(resp, &ok, &reason);

       // 判定响应是否成功
        if (!ok)
            {
            LOG() << "[获取最近消息] 失败! reason = " << reason;
            return;
        }

        // 把拿到的数据,设置到 DataCenter 中
        dataCenter->resetRecentMessageList(chatSessionId, pbResp);

        // 发送信号,告知界面进行更新
        if (updateUi)
        {
            emit dataCenter->getRecentMessageListDone(chatSessionId);
        }
        else
        {
            emit dataCenter->getRecentMessageListDoneNoUi(chatSessionId);
        }

    });

    //

}

// 此处 extraInfo 存放传递扩展信息 ,尤其是对文件消息来说, 通过这个字段表示 文件名，
// 其他类型消息暂时不涉及, 就直接设置为空, 如果后续有消息类型需要这个参数, 就可以赋予一定的特殊含义
void NetClient::sendMessage(const QString &loginSessionId, const QString &chatSessionId,MessageType messageType,
                            const QByteArray &content, const QString& extraInfo)
{
    // 1 通过 protobuf 构造 body
    shiyue_im::NewMessageReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);


    // 构造 MessageContent
    shiyue_im::MessageContent messageContent;
    if(messageType == MessageType::TEXT_TYPE)
    {
        messageContent.setMessageType(shiyue_im::MessageTypeGadget::MessageType::STRING) ;

        shiyue_im::StringMessageInfo stringMessageInfo;
        stringMessageInfo.setContent(content);
        messageContent.setStringMessage(stringMessageInfo);
    }
    else if (messageType == MessageType::IMAGE_TYPE)
    {
        messageContent.setMessageType(shiyue_im::MessageTypeGadget::MessageType::IMAGE);

        shiyue_im::ImageMessageInfo imageMessageInfo;
        imageMessageInfo.setFileId(""); //fileId 是文件在服务器存储的时候, 生成的 id 此时还无法获取到 暂时填成""
        imageMessageInfo.setImageContent(content);
        messageContent.setImageMessage(imageMessageInfo);
    }
    else if (messageType == MessageType::FILE_TYPE)
    {
        messageContent.setMessageType(shiyue_im::MessageTypeGadget::MessageType::FILE);

        shiyue_im::FileMessageInfo fileMessageInfo;
        fileMessageInfo.setFileId("");   // fileId 是文件在服务器存储的时候, 生成的 id ,此时还无法获取到 暂时填成""
        fileMessageInfo.setFileSize(content.size());
        fileMessageInfo.setFileName(extraInfo);
        fileMessageInfo.setFileContents(content);
        messageContent.setFileMessage(fileMessageInfo);
    }
    else if (messageType == MessageType::SPEECH_TYPE)
    {
        messageContent.setMessageType(shiyue_im::MessageTypeGadget::MessageType::SPEECH);

        shiyue_im::SpeechMessageInfo speechMessageInfo;
        speechMessageInfo.setFileId("");   // fileId 是文件在服务器存储的时候, 生成的 id ,此时还无法获取到 暂时填成""
        speechMessageInfo.setFileContents(content);
        messageContent.setSpeechMessage(speechMessageInfo);
    }
    else
    {
        LOG() << "错误的消息类型 messageType=" <<messageType;
    }


    req.setMessage(messageContent);

    // 序列化
    QByteArray body = req.serialize(&serializer);
    LOG() << "[发送消息] 发送请求 requestId=" <<req.requestId() << ", loginSessionId=" << req.sessionId() <<
        ",chatSessionId=" << req.chatSessionId() << ", messageType=" <<req.message().messageType();

    // 2 发送 HTTP 请求
    QNetworkReply* resp = this->sendHttpRequest("/service/message_transmit/new_message", body);

    // 3 处理 HTTP 响应

    connect(resp, &QNetworkReply::finished, this,[=](){
        // 针对响应进行解析
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::NewMessageRsp>(resp, &ok, &reason);

        //判定响应 是否正确
        if (!ok)
        {
            LOG() << "[发送消息] 处理出错! reason=" << reason;
            return;
        }

        // 此处只是记录 成功失败, 不需要把内容写到 DataCenter 中。

        //通知调用者, 响应处理完毕
        // 后续是需要把用户自己发的消息内容显示到消息展示区
        //就需要处理信号的函数,拿到用户发送消息的消息内容
        // 为了传递内容给信号处理函数,就可以把消息内容做为信号的参数给带过去
        emit dataCenter->sendMessageDone(messageType, content, extraInfo);

        //打印日志
        LOG() << "[发送消息] 响应处理完毕! requestId =" << pbResp->requestId();

    });

}

void NetClient::receiveMessage(const QString& chatSessionId)
{
    // 1.先需要判断一下,当前这个收到的消息对应的会话,是否是正在被用户选中的'当前会话'
    // 当前会话，就需要把消息显示到消息显示区,也需要更新会话列表的消息预览
    // 不是当前对话，只需要更新会话列表中的消息预览，并且更新"未读消息数目"

    if (chatSessionId == dataCenter->getCurrentChatSessionId()) {
        //收到的消息会话就是选中的会话
        //在消息展示区新增消息
        const Message& lastMessage  = dataCenter->getRecentMessageList(chatSessionId)->back();
        //通过信号， 让NetClient 模块，能够通知界面(消息展示区)
        emit dataCenter->receiveMessageDone(lastMessage);

    } else {
        //收到的消息会话不是选中会话
        //更新未读消息数目
        dataCenter->addUnread(chatSessionId);
    }

    //统一更新会话列表的消息预览
    emit dataCenter->updateLastMessage(chatSessionId);
}

void NetClient::changeNickName(const QString &loginSessionId, const QString &nickname)
{
    // 1. 通过 pb构造请求
    shiyue_im::SetUserNicknameReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setNickname(nickname);
    QByteArray body = req.serialize(&serializer);
    LOG() << "修改用户昵称发送请求 requestId =  " << req.requestId() << ", loginSessionid = " <<  req.sessionId()\
        << ",nickname = " << req.nickname();

    // 2. 发送 http 请求
    QNetworkReply* resp  = this->sendHttpRequest("/service/user/set_nickname", body);

    // 3. 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        // a.解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::SetUserNicknameRsp>(resp, &ok, &reason);

       // b. 判定是否出错
        if (!ok) {
            LOG() << "[修改用户昵称出错! reason = " << reason;
            return;
        }

        // c.把数据设置到 DataCenter 里面, 这里处理和前面不一样
        dataCenter->resetNickname(nickname);

        //d. 发送信号通知调用者处理完毕
        emit dataCenter->changeNicknameDone();

        //e. 打印日志
        LOG() << "[修改用户昵称] 处理响应完毕！ requestId= " <<pbResp->requestId();

    });

}

void NetClient::changeDescription(const QString &loginSessionId, const QString &desc)
{
    // 通过 protobuf构造请求body
    shiyue_im::SetUserDescriptionReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    pbReq.setDescription(desc);
    QByteArray body =  pbReq.serialize(&serializer);

    LOG() << "[修改签名] 发送请求 requestId = " << pbReq.requestId() << ", loginSessionId= " << pbReq.sessionId()\
          << ", desc = " << pbReq.description();
    // 2.发送 http 请求
    QNetworkReply* resp = this->sendHttpRequest("/service/user/set_description", body);

    //3.处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){

        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::SetUserDescriptionRsp>(resp, &ok, &reason);
        //判断响应是否成功
        if (!ok) {
            LOG() << "[修改签名] 响应失败! reason=" << reason;
            return;
        }

        // 把得到的结果,写入 DataCenter
        dataCenter->resetDescription(desc);

        //发送信号,通知修改完成
        emit dataCenter->changeDescriptionDone();

        // 打印日志
        LOG() << "[修改签名]响应完成! requestId = " << pbResp->requestId();

    });

}

void NetClient::getVerifyCode(const QString &phone)
{
    //1.构造请求body
    shiyue_im::PhoneVerifyCodeReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setPhoneNumber(phone);
    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[获取手机验证码]发起请求 requestId=" << pbReq.requestId() << ", phone="<< phone;

    // 2.发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/user/get_phone_verify_code", body);

    //3. 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::PhoneVerifyCodeRsp>(resp, &ok, &reason);

        //判定响应是否成功
        if (!ok) {
            LOG() << "获取手机验证码失败！ reason" << reason;
            return;
        }
        //保存数据到DataCenter
        dataCenter->resetVerifyCodeId(pbResp->verifyCodeId());
        //发送信号通知调用者
        emit dataCenter->getVerifyCodeDone();

        //打印日志
        LOG() << "[获取手机验证码]响应完成 requestId = " << pbResp->requestId();});

}

void NetClient::changePhone(const QString &loginSessionId, const QString &phone, const QString &verifycodeId, const QString &verifyCode)
{
    // 1. 构造请求body
    shiyue_im::SetUserPhoneNumberReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    pbReq.setPhoneNumber(phone);
    pbReq.setPhoneVerifyCode(verifyCode);
    pbReq.setPhoneVerifyCodeId(verifycodeId);

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[修改手机号]发送请求 requestId = " << pbReq.requestId() <<", loginSessionId" << pbReq.sessionId() \
          <<", phone= " <<pbReq.phoneNumber() << ", verifyCodeId=" << pbReq.phoneVerifyCodeId() \
          <<", verifyCode=" << pbReq.phoneVerifyCode();

    // 2. 发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/user/set_phone", body);

    // 3. 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;

        auto pbResp = this->handleHttpResponse<shiyue_im::SetUserPhoneNumberRsp>(resp, &ok, &reason);
        //判断响应是否正确
        if (!ok) {
            LOG() << "[修改手机号] 响应失败! reason= " << reason;
            return;
        }

        //把结果记录到dataCenter中
        dataCenter->resetPhone(phone);

        //发送信号,通知调用者完成
        emit dataCenter->changePhoneDone();

        //打印日志
        LOG() << "[修改手机号] 响应完成 requestId= "<< pbResp->requestId();

    });


}

void NetClient::changeAvatar(const QString &loginSessionId, const QByteArray &avatar)
{
    // 1.构造请求body
    shiyue_im::SetUserAvatarReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    pbReq.setAvatar(avatar);
    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[修改头像] 发送请求 requestId=" <<pbReq.requestId() << ", loginSessionId=" <<pbReq.sessionId();

    // 2. 发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/user/set_avatar", body);

    // 3.处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::SetUserAvatarRsp>(resp, &ok, &reason);

        //判定响应结果是否正确
        if (!ok) {
            LOG() << "[修改头像] 响应出错! reason=" << reason;
            return;
        }
        //把数据设置到DataCenter中
        dataCenter->resetAvatar(avatar);

        //发送信号
        emit dataCenter->changeAvatarDone();

        //打印日志
        LOG() << "[修改头像] 响应完成 requestId = " <<  pbResp->requestId();

    });
}

void NetClient::deleteFriend(const QString &loginSessionId, const QString &userId)
{
    //1.构造请求body
    shiyue_im::FriendRemoveReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    pbReq.setPeerId(userId);

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[删除好友] 发送请求 requestId=" << pbReq.requestId() << ", loginSession" << pbReq.sessionId()\
          <<", peerId= " << pbReq.peerId();

    // 2. 发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/friend/remove_friend", body);

    // 3. 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::FriendRemoveRsp>(resp, &ok, &reason);
        if (!ok) {
            LOG() << "[删除好友]响应失败! reason=" <<reason;
            return;
        }

        //把结果写入DataCenter,把该删除的用户,从好友列表中删除
        dataCenter->removeFriend(userId);

        // 发送信号,通知调用者当前好友删除完毕
        emit dataCenter->deleteFriendDone();

        //打印日志
        LOG() << "[删除好友] 响应完成 requestId=" << pbResp->requestId();
    });
}

void NetClient::addFriendApply(const QString &loginSessionId, const QString &userId)
{
    // 1.构造请求body
    shiyue_im::FriendAddReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    pbReq.setUserId(userId);

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[添加好友申请] 发送请求 requestId=" << pbReq.requestId() <<", loginSessionId=" << pbReq.sessionId()\
          <<"userId=" << pbReq.userId();

    // 2.发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/friend/add_friend_apply", body);

    // 3. 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::FriendAddRsp>(resp, &ok, &reason);
        if (!ok) {
            LOG() << "[添加好友申请]响应失败! reason=" << reason;
            return;
        }
        //记录结果到DataCenter,此处不需要记录任何数据

        //发送信号，通知调用者
        emit dataCenter->addFriendApplyDone();

        //打印日志
        LOG() << "[添加好友申请] 响应完毕 requestId=" << pbResp->requestId();


    });
}

void NetClient::acceptFriendApply(const QString &loginSessionId, const QString &userId)
{

}

void NetClient::rejectFriendApply(const QString &loginSessionId, const QString &userId)
{
    //1. 构造请求body

    shiyue_im::FriendAddProcessReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    pbReq.setAgree(false);
    pbReq.setApplyUserId(userId);

    QByteArray body = pbReq.serialize(&serializer);

    LOG() << "[拒绝好友申请] 发送请求 requestId= " << pbReq.requestId() <<", loginSessionId" << pbReq.sessionId()\
          <<",userId=" << pbReq.applyUserId();

    // 2. 发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/friend/add_friend_process", body);

    //3.处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::FriendAddProcessRsp>(resp, &ok, &reason);
        if (!ok) {
            LOG() << "[拒绝好友申请] 处理失败！reason=" <<reason;
            return;
        }
        //此处做一个好友列表的更新
        // 把数据从好友申请列表中删除，不会更新好友列表,但是需要把这个对象从好友申请列表删除

        dataCenter->removeFromApplyList(userId);
        //发送信号通知界面进行更新
        emit dataCenter->rejectFriendApplyDone();

        //打印日志
        LOG() << "[拒绝好友申请] 响应完成! requestId=" << pbResp->requestId();
    });



}

void NetClient::createGroupChatSession(const QString &loginSessionId, const QList<QString> &userIdList)
{
    //创建单聊,是在添加好友成功之后,服务器自主创建的,不需要客户端主动触发,这个接口就只需要用于创建群聊了
    // 1.构造请求body
    shiyue_im::ChatSessionCreateReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setChatSessionName("群聊");
    pbReq.setSessionId(loginSessionId);
    pbReq.setMemberIdList(userIdList);

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[创建群聊会话] 发送请求 requestId=" << pbReq.requestId() << ", loginSessionId" << loginSessionId\
          <<"userIdList=" << userIdList;
    // 2.发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/friend/create_chat_session", body);

    // 3.处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::ChatSessionCreateRsp>(resp, &ok, &reason);

        //判断结果是否正确
        if (!ok) {
            LOG() << "[创建群聊会话] 响应失败！ reason=" << reason;
            return;
        }

        //往DataCenter存储数据, 由于此处创建的会话,是websockt推送过来的
        //在这里无需更新DataCenter 后续通过 websockt 逻辑来更新即可

        //通知调用者, 响应处理完毕
        emit dataCenter->createGroupChatSessionDone();

        //打印日志
        LOG() << "[创建群聊会话] 响应完成 requestId=" << pbResp->requestId();
    });

}

void NetClient::getMemberList(const QString &loginSessionId, const QString &chatSessionId)
{
    //1. 构造请求 body
    shiyue_im::GetChatSessionMemberReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    pbReq.setChatSessionId(chatSessionId);

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[获取会话成员列表] requestId=" << pbReq.requestId() << ", loginSessionId=" << pbReq.sessionId()\
          <<", chatSessionId=" << pbReq.chatSessionId();

    // 发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/friend/get_chat_session_member", body);

    //处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::GetChatSessionMemberRsp>(resp, &ok, &reason);

        //判断结果是否正确
        if (!ok) {
            LOG() << "[获取会话成员列表]响应失败 reason =" << reason;
            return;
        }

        //把结果记录到Datacenter
        dataCenter->resetMemberList(chatSessionId, pbResp->memberInfoList());

        // 发送信号
        emit dataCenter->getMemberListDone(chatSessionId);

        //打印日志
        LOG() << "[获取会话成员列表] 响应完成 requestId= " << pbResp->requestId();
    });
}

void NetClient::searchUser(const QString &loginSessionId, const QString &searchKey)
{
    // 1.构造请求body
    shiyue_im::FriendSearchReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    pbReq.setSearchKey(searchKey);

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[搜索用户] 发送请求 requestId=" << pbReq.requestId() << ", loginSessionId=" << loginSessionId\
          <<",searchKey=" << searchKey;

    // 2.发送Http请求
     QNetworkReply* resp = this->sendHttpRequest("/service/friend/search_friend", body);

    // 3. 连接信号槽
     connect(resp, &QNetworkReply::finished, this, [=](){
        // 解析响应
         bool ok = false;
        QString reason;
         auto pbResp = this->handleHttpResponse<shiyue_im::FriendSearchRsp>(resp, &ok, &reason);

        //判定响应成功
        if (!ok) {
             LOG() << "[搜索用户] 响应失败 reason=" << reason;
            return;
        }

        // 把得到的结果记录到DataCenter
        dataCenter->resetSearchUserResult(pbResp->userInfo());

        //发送信号通知调用者
        emit dataCenter->searchUserDone();

        //打印日志
        LOG() << "[搜索用户] 响应完成 requestId= " <<pbResp->requestId();

     });

}

void NetClient::searchMessage(const QString& loginSessionId, const QString& chatSessionId, const QString& searchKey)
{
    // 1.构造请求body
    shiyue_im::MsgSearchReq pbReq;
    pbReq.setSearchKey(searchKey);
    pbReq.setSessionId(loginSessionId);
    pbReq.setRequestId(makeRequestId());
    pbReq.setChatSessionId(chatSessionId);

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[按关键词搜索历史消息] 发送请求 requestId=" << pbReq.requestId() << ", loginSessionId= " <<pbReq.sessionId()\
          <<", chatSessionId=" << pbReq.chatSessionId() << ", searchKey=" <<searchKey;

    //2. 发送HTTP请求
    QNetworkReply* resp = this->sendHttpRequest("/service/message_storage/search_history", body);

    // 3. 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;

        auto pbResp = this->handleHttpResponse<shiyue_im::MsgSearchRsp>(resp, &ok, &reason);

        //判定响应是否正确
        if (!ok) {
            LOG() << "[按关键词搜索历史消息] 响应失败! reason=" << reason;
            return;
        }

        //把响应结果写入DataCenter
        dataCenter->resetSearchMessageResult(pbResp->msgList());

        // 发送信号
        emit dataCenter->searchMessageDone();

        //打印日志
        LOG() << "[按关键字搜索历史消息] 响应完成 requestId=" << pbResp->requestId();

    });
}

void NetClient::searchMessageByTime(const QString& loginSessionId, const QString& chatSessionId, const QDateTime& begTime, const QDateTime& endTime)
{
    // 1.构造请求 bdoy
    shiyue_im::GetHistoryMsgReq pbReq;
    pbReq.setChatSessionId(chatSessionId);
    pbReq.setSessionId(loginSessionId);
    pbReq.setRequestId(makeRequestId());
    //通过秒级时间戳表示
    pbReq.setStartTime(begTime.toSecsSinceEpoch());
    pbReq.setOverTime(endTime.toSecsSinceEpoch());

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[按时间搜索历史消息] 发送请求 requestId=" << pbReq.requestId() << ", loginSessionId=" << loginSessionId\
          <<", chatSessionId=" << chatSessionId << ", begTime=" << begTime << ", endTime" << endTime;

    // 2. 发送HTTP请求
    QNetworkReply* resp = this->sendHttpRequest("/service/message_storage/get_xhistory", body);

    // 3. 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok =false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::GetHistoryMsgRsp>(resp, &ok, &reason);

        //判定响应结果是否正确
        if (!ok) {
            LOG() << "[按时间搜索历史消息]响应失败 reason=" << reason;
            return;
        }

        //把响应结果记录到datacenter中
        dataCenter->resetSearchMessageResult(pbResp->msgList());

        //发送信号通知调用者
        emit dataCenter->searchMessageDone();

        //打印日志
        LOG() << "[按时间搜索历史消息] 响应完成 requestId=" << pbResp->requestId();



    });

}

void NetClient::userLogin(const QString &username, const QString &password)
{
    // 1. 构造请求body
    shiyue_im::UserLoginReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setNickname(username);
    pbReq.setPassword(password);
    pbReq.setVerifyCode("");
    pbReq.setVerifyCodeId("");

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[用户名密码登录] 发送请求 requestId= " << pbReq.requestId() << ", username=" << pbReq.nickname()\
          <<",passWord=" << pbReq.password();

    //2.发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/user/username_login", body);

    //3.处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp =this->handleHttpResponse<shiyue_im::UserLoginRsp>(resp, &ok, &reason);

        //判定响应结果是否正确
        if (!ok) {
            LOG() << "[用户名登录] 处理失败 reason=" << reason;
            emit dataCenter->userLoginDone(false, reason);
            return;
        }

        //记录一下返回的数据
        dataCenter->resetLoginSessionId(pbResp->loginSessionId());

        //发送信号,通知调用者处理完毕
        emit dataCenter->userLoginDone(true, "");

        //打印日志
        LOG() << "[用户名登录] 处理响应 requestId=" << pbResp->requestId();
    });

}

void NetClient::userRegister(const QString &username, const QString &password)
{
    // 1.构造请求body
    shiyue_im::UserRegisterReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setNickname(username);
    pbReq.setPassword(password);
    pbReq.setVerifyCode("");
    pbReq.setVerifyCodeId("");

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[用户名注册] 发送请求 requestId=" << pbReq.requestId() << ", username=" << pbReq.nickname() \
          <<", password=" << pbReq.password();

    //2. 发送HTTP请求
    QNetworkReply* resp  = this->sendHttpRequest("/service/user/username_register", body);

    //3.处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应body
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::UserRegisterRsp>(resp, &ok, &reason);

        //判定响应结果是否正确
        if (!ok) {
            LOG() << "[用户名注册]响应是失败 reason=" << reason;
            emit dataCenter->userRegisterDone(false, reason);
            return;
        }

        //返回的数据保存到dataCenter
        //对于注册来说,不需要保存任何信息,直接跳过这个环节

        //通知调用者响应处理完成
        emit dataCenter->userRegisterDone(true, "");

        //打印日志
        LOG() <<"[用户名注册] 响应完成 requestId=" << pbResp->requestId();


    });

}

void NetClient::phoneLogin(const QString &phone, const QString &verifyCodeId, const QString &verifyCode)
{
    // 1.构造请求body
    shiyue_im::PhoneLoginReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setPhoneNumber(phone);
    pbReq.setVerifyCodeId(verifyCodeId);
    pbReq.setVerifyCode(verifyCode);

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[手机号登录] 发送请求 requestId=" << pbReq.requestId() << ", phone=" << pbReq.phoneNumber()
          <<",verifycodeId=" << pbReq.verifyCodeId() << ", verifyCode=" << pbReq.verifyCode();

    // 2. 发送Http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/user/phone_login", body);

    // 3. 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;

        auto pbResp =this->handleHttpResponse<shiyue_im::PhoneLoginRsp>(resp, &ok, &reason);
        if (!ok) {
            LOG() << "[手机号登录] 响应出错! reason =" << reason;
            emit dataCenter->phoneLoginDone(false, reason);
            return;
        }

        //把相应结果记录到DataCenter;
        dataCenter->resetLoginSessionId(pbResp->loginSessionId());

        //发送信号
        emit dataCenter->phoneLoginDone(true, "");

        //打印日志
        LOG() << "[手机号登录] 响应完毕 requestId="  << pbResp->requestId();
    });
}

void NetClient::phoneRegister(const QString &phone, const QString &verifyCodeId, const QString &verifyCode)
{
    //1.构造请求Body
    shiyue_im::PhoneRegisterReq pbReq;
    pbReq.setPhoneNumber(phone);
    pbReq.setRequestId(makeRequestId());
    pbReq.setVerifyCode(verifyCode);
    pbReq.setVerifyCodeId(verifyCodeId);

    QByteArray body = pbReq.serialize(&serializer);
    LOG() << "[手机号注册]发送请求 requestId = " << pbReq.requestId() << ", phone=" << pbReq.phoneNumber()\
          <<", verifyCodeId" << pbReq.verifyCodeId() << ", verifyCode" << pbReq.verifyCode();

    // 2.发送http请求
    QNetworkReply* resp =  this->sendHttpRequest("/service/user/phone_register", body);

    //3. 处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp =this->handleHttpResponse<shiyue_im::PhoneRegisterRsp>(resp, &ok, &reason);

        //判定响应是否成功
        if (!ok) {
            LOG() << "[手机号注册] 响应失败！ reason=" << reason;
            emit dataCenter->phoneRegisterDone(false, reason);
            return;
        }

        //dataCenter 记录结果, 注册操作不需要记录

        //发送信号
        emit dataCenter->phoneRegisterDone(true, "");


        //打印日志
        LOG() << "[手机号注册]响应完成 requestId=" << pbResp->requestId();
    });

}

void NetClient::getSingleFile(const QString &loginSessionId, const QString &fileId)
{
    // 1.构造请求body
    shiyue_im::GetSingleFileReq pbReq;
    pbReq.setFileId(fileId);
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    QByteArray body = pbReq.serialize(&serializer);

    LOG() << "[获取文件内容] 发送请求 requestId=" << pbReq.requestId() << ", fileId=" << pbReq.fileId();
    // 2. 发送HTTP请求
    QNetworkReply* resp = this->sendHttpRequest("/service/file/get_single_file", body);

    //3.处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::GetSingleFileRsp>(resp, &ok, &reason);

        //判定响应结果
        if (!ok) {
            LOG() <<"[获取文件内容] 响应失败 reason=" << reason;
            return;
        }

        //响应结果保存，之前都是把结果保存到DataCenter的
        //这里涉及到的文件可能会很多, 不使用DataCenter保存
        //直接通过  信号把文件数据,投送到调用者的位置上

        //发送信号
        emit dataCenter->getSingleFileDone(fileId, pbResp->fileData().fileContent());

        //打印日志
        LOG() << "[获取文件内容] 响应完成 requestId=" << pbResp->requestId();


    });
}

void NetClient::speechConvertText(const QString &loginSessionId, const QString &fileId, const QByteArray &content)
{
    // 1.构造请求body
    shiyue_im::SpeechRecognitionReq pbReq;
    pbReq.setRequestId(makeRequestId());
    pbReq.setSessionId(loginSessionId);
    pbReq.setSpeechContent(content);
    QByteArray body = pbReq.serialize(&serializer);

    LOG() << "[语音转文字] 发送请求 requestId=" << pbReq.requestId() <<", loginSessionId" << pbReq.sessionId();

    // 2. 发送http请求
    QNetworkReply* resp = this->sendHttpRequest("/service/speech/recognition", body);


    //3.处理响应
    connect(resp, &QNetworkReply::finished, this, [=](){
        //解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<shiyue_im::SpeechRecognitionRsp>(resp, &ok, &reason);

        //判断响应结果
        if (!ok) {
            LOG() << " [语音转文字]响应错误 reason=" << reason;
            return;
        }

        //把结果写入dataCenter中,此处不打算通过DataCenter表示这里的语音识别结果,直接通过信号通知结果即可


        //发送信号,通知调用者
        emit dataCenter->speechConvertTextDone(fileId, pbResp->recognitionResult());

        //打印日志
        LOG() << "[语音转文字] 响应完成 requestId= " << pbResp->requestId();
    });
}



} // end network
;
